package com.huanglexin.framework;

import com.huanglexin.framework.annotation.Component;
import com.huanglexin.framework.annotation.ComponentScan;
import com.huanglexin.framework.annotation.Scope;
import com.huanglexin.framework.exception.BeanNotExistException;
import com.huanglexin.framework.exception.CreateBeanException;
import com.huanglexin.framework.exception.IllegalBeanScopeException;
import com.huanglexin.framework.util.StringUtil;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;

/**
 * @author Huang Lexin
 * @date 2021年11月21日 22:38
 */
public class MyApplicationContext {

    private final String SINGLETON = "singleton";

    private final String PROTOTYPE = "prototype";

    private final Logger logger = Logger.getLogger("com.huanglexin.framework.MyApplicationContext");

    private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();

    private final ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

    private Class<?> configureClass;


    public MyApplicationContext(Class<?> configureClass) {
        this.configureClass = configureClass;
        // 解析配置类, 扫描所有 Bean 并生成 BeanDefinition 加入到 beanDefinitionMap
        scan(configureClass);
        for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
            String beanName = entry.getKey();
            BeanDefinition beanDefinition = entry.getValue();
            if (SINGLETON.equalsIgnoreCase(beanDefinition.getScope())) {
                Object bean = createBean(entry.getValue());
                singletonObjects.put(beanName, bean);
            }
        }
    }

    private Object createBean(BeanDefinition beanDefinition) {
        Class<?> classz = beanDefinition.getClassz();
        try {
            return classz.getConstructor().newInstance();
        } catch (InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {
            throw new CreateBeanException("创建 " + classz.getSimpleName() + " 对象失败!");
        }
    }

    private void scan(Class<?> configClass) {
        ComponentScan componentScanAnnotation = configClass.getDeclaredAnnotation(ComponentScan.class);
        if (null != componentScanAnnotation) {
            String path = componentScanAnnotation.value();
            logger.info("pathToScan = " + path);
            // 根据扫描路径找到该资源
            ClassLoader classLoader = MyApplicationContext.class.getClassLoader();
            String temp = path.replace('.', '/');
            URL resource = classLoader.getResource(temp);
            // 注意, 路径名称中不能含有中文, 否则代码中的实际路径将会变成长编码, 找不到指定的文件
            File file = new File(resource.getFile());
            scanClasses(path, classLoader, file);
        }
    }

    private void scanClasses(String path, ClassLoader classLoader, File file) {
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            if (null != files && 0 != files.length) {
                for (File file1 : files) {
                    scanClasses(path, classLoader, file1);
                }
            }
        } else {
            String absolutePath = file.getAbsolutePath();
            if (absolutePath.endsWith(".class")) {
                String className =
                        absolutePath.substring(absolutePath.indexOf(path.replace('.', '\\')),
                                absolutePath.indexOf(".class")).replace('\\', '.');
                try {
                    Class<?> classz = classLoader.loadClass(className);
                    resolveBeanDefinition(classz);
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void resolveBeanDefinition(Class<?> classz) {
        Component componentAnnotation = classz.getDeclaredAnnotation(Component.class);
        if (componentAnnotation != null) {
            BeanDefinition beanDefinition = new BeanDefinition();
            Scope scopeAnnotation = classz.getDeclaredAnnotation(Scope.class);
            // if neither SINGLETON, nor PROTOTYPE, throw exception
            String scope = resolveScope(scopeAnnotation);
            beanDefinition.setScope(scope);
            beanDefinition.setClassz(classz);
            String beanName = "".equals(componentAnnotation.value()) ?
                    StringUtil.solveBeanName(classz.getSimpleName()) : componentAnnotation.value();
            beanDefinitionMap.put(beanName, beanDefinition);
        }
    }

    private String resolveScope(Scope scopeAnnotation) {
        if (null != scopeAnnotation && !SINGLETON.equalsIgnoreCase(scopeAnnotation.value())) {
            if (PROTOTYPE.equalsIgnoreCase(scopeAnnotation.value())) {
                return PROTOTYPE;
            }
            throw new IllegalBeanScopeException("illegal bean scope for " + scopeAnnotation.value());
        }
        return SINGLETON;
    }


    public Object getBean(String beanName) {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (beanDefinition == null) {
            throw new BeanNotExistException("bean with name " + beanName + " not exists!");
        }
        if (SINGLETON.equalsIgnoreCase(beanDefinition.getScope())) {
            return singletonObjects.get(beanName);
        }
        // create Bean ?
        return createBean(beanDefinition);
    }
}
